/**
 * @author 14544
 * @date 2020/8/4 11:59
 */
(function () {
    /**
     * 执行then存放的方法，为了确定then中存放了方法，需要异步执行
     * 根据不同实例状态执行对应的函数
     */
    const noticeThen = function () {
        let timer = setTimeout(() => {
            clearTimeout(timer);
            timer = null;
            if (this.PromiseStatus === 'resolved') {
                this.resolveThenFun && this.resolveThenFun.call(this, this.PromiseValue)
            }
            if (this.PromiseStatus === 'rejected') {
                this.rejectThenFun && this.rejectThenFun.call(this, this.PromiseValue)
            }
        })
    }

    /**
     * 更改实例的值和状态，同时通知then中存放的方法执行
     * @param value 执行resolve或reject函数时传入的值，表示promise实例结果值
     * @param status 实例状态只能改变一次，为resolved或rejected
     */
    const changeStatus = function (value, status) {
        if (this.PromiseStatus !== 'pending') return;
        this.PromiseStatus = status;
        this.PromiseValue = value;

        noticeThen.call(this);
    }

    class WPromise {
        constructor(executor) {
            // 如果传入的参数不是一个函数，则抛出错误
            if (typeof executor !== 'function') {
                throw new TypeError(`WPromise resolver ${executor} is not a function`)
            }

            // 初始化实例状态
            this.PromiseStatus = 'pending';
            this.PromiseValue = undefined;
            const _this = this;

            //立即执行函数，传入两个函数参数，一个表示执行成功，一个表示执行失败，执行此回调函数时就会改变实例的value和status
            // 同时需要监测代码执行是否报错，代码报错也表示执行失败
            try {
                executor(function (value) {
                    changeStatus.call(_this, value, 'resolved')
                }, function (reason) {
                    changeStatus.call(_this, reason, 'rejected')
                })
            } catch (e) {
                changeStatus.call(_this, e, 'rejected')
            }
        }

        /**
         * 存放两个函数方法，实例状态为resolved执行第一个方法，rejected执行第二个方法
         * @param resolveThenFun
         * @param rejectThenFun
         * @returns {WPromise} 返回一个Promise实例
         */
        then(resolveThenFun, rejectThenFun) {
            // 没有传入回调函数时，默认创建一个，此函数执行后会返回与该实例相同状态和值的新实例
            if (typeof resolveThenFun !== 'function') {
                resolveThenFun = WPromise.resolve;
            }
            if (typeof rejectThenFun !== 'function') {
                rejectThenFun = WPromise.reject;
            }
            const _this = this;
            // 返回一个promise实例
            return new WPromise((resolve, reject) => {
                // 将传入的回调函数，为了检测代码执行错误，重新使用函数包裹起来，同时在实例上创建两个属性存放这两个方法
                _this.resolveThenFun = function (value) {
                    executeCallback(resolveThenFun, value);
                }
                _this.rejectThenFun = function (reason) {
                    executeCallback(rejectThenFun, reason);
                }

                /**
                 * 在resolve或reject函数执行后，就会执行then中存放的方法，也就是下面这个方法
                 * @param callback then中存放的回调函数
                 * @param value 实例的PromiseValue
                 */
                function executeCallback(callback, value) {
                    // 监听此回调函数执行是否报错，报错直接返回一个失败状态的实例，值是报错的原因
                    try {
                        const result = callback(value);
                        // 代码不报错，接受返回值， 如果返回值不是一个Promise实例，则返回一个成功状态的实例，值就是该返回值
                        // 如果返回值是一个Promise实例，那么再次使用then方法，将当前resolve，reject函数存入
                        // 如果Promise实例状态成功就会执行第一个方法，那么最终就会返回成功状态的的新Promise实例
                        // 如果Promise实例状态失败就会执行第二个方法，那么最终就会返回失败状态的新Promise实例
                        if (!(result instanceof WPromise)) {
                            resolve(result);
                        } else {
                            result.then(resolve, reject);
                        }
                    } catch (e) {
                        reject(e)
                    }

                    // 执行完成后，移除这两个属性，不暴露在最终实例上
                    delete _this.resolveThenFun;
                    delete _this.rejectThenFun;
                }
            })
        }

        /**
         * catch方法就是then中只存放了第二个方法的简写，专门用来捕获错误
         * @param callback 传入的回调函数
         * @returns {WPromise} 返回新Promise实例
         */
        catch(callback) {
            return this.then(null, callback);
        }

        /**
         * finally 表示无论实例最终成功或失败都会执行此方法
         * @param callback 传入的回调函数
         * @returns {WPromise} 返回新Promise实例
         */
        finally(callback) {
            // 如果传入的不是函数，直接返回当前实例
            if (typeof callback !== 'function') return this;
            // 因为不知道实例最终的状态如何，所以要使用实例的then方法
            // 存入两个方法，实例状态成功或失败自然就会执行对应回调
            return this.then(function (value) {
                // 如果传入的回调函数报错，就返回一个状态失败，值为报错原因的promise实例
                try {
                    callback();
                    // 代码不报错，返回实例成功状态值value的Promise实例。与回调函数的返回值无关
                    return WPromise.resolve(value);
                } catch (e) {
                    return WPromise.reject(e);
                }
            }, function (reason) {
                // 如果传入的回调函数报错，就返回一个状态失败，值为报错原因的promise实例
                try {
                    callback();
                    // 代码不报错，返回实例失败状态值reason的Promise实例。与回调函数的返回值无关
                    return WPromise.reject(reason);
                } catch (e) {
                    return WPromise.reject(e);
                }
            })
        }

        /**
         * new Promise() 的快捷方法，返回一个状态成功，值为传入的value的Promise实例
         * @param value 传入的value
         * @returns {WPromise} 返回新Promise实例
         */
        static resolve(value) {
            return new WPromise((resolve,reject) => {
                // 如果传入是一个Promise实例，则使用then方法，返回其最终的状态和值的Promise实例
                if (value instanceof WPromise) {
                    value.then(resolve,reject)
                } else {
                    // 传入是一个不是Promise实例，返回一个状态成功，值为传入的value的Promise实例
                    resolve(value);
                }
            })
        }

        /**
         * new Promise() 的快捷方法，返回一个状态失败，值为传入的reason的Promise实例
         * @param reason 传入的reason
         * @returns {WPromise} 返回新Promise实例
         */
        static reject(reason) {
            return new WPromise((resolve, reject) => {
                if (reason instanceof WPromise) {
                    reason.then(resolve,reject)
                } else {
                    reject(reason);
                }
            })
        }

        /**
         * 当传入的Promise实例的状态都成功时，此返回一个实例状态成功，值为数组的Promise实例
         *  有一个失败，则立即返回失败的Promise实例
         * @param promiseArr 由Promise实例组成的数组
         * @returns {WPromise} 返回新Promise实例
         */
        static all(promiseArr) {
            let resultArr = []
            let index = 0
            return new WPromise((resolve, reject) => {
                for (let i = 0; i < promiseArr.length; i++) {
                    const promiseElement = promiseArr[i];
                    if (promiseElement instanceof WPromise) {
                        promiseElement.then(res=>{
                            checkFinish(i,res)
                        }).catch(e=>reject(e))
                    } else {
                        checkFinish(i,promiseElement)
                    }
                }
                // 当值数组与传入的promise实例数组相等时，表示所有实例状态都成功了，这时就可以resolve了
                function checkFinish(i,value){
                    resultArr[i] = value
                    index ++
                    if (index === promiseArr.length) {
                        resolve(resultArr)
                    }
                }
            })
        }

        /**
         * 当传入的Promise实例的状态有一个最先成功或失败时，就立即返回这个成功或失败的Promise实例
         * @param promiseArr 由Promise实例组成的数组
         * @returns {WPromise} 返回新Promise实例
         */
        static race(promiseArr) {
            return new WPromise((resolve, reject) => {
                for (let i = 0; i < promiseArr.length; i++) {
                    const item = promiseArr[i];
                    // 如果是Promise实例
                    if (item instanceof WPromise) {
                        // 使用then存入两个方法，实例状态成功就resolve，否则reject
                        item.then(value => {
                            resolve(value)
                        }, reason => {
                            reject(reason)
                        })
                    } else {
                        // 如果不是Promise实例，则立即resolve
                        resolve(item)
                    }
                }
            })
        }
    }
    if(typeof window !== 'undefined') {
        window.WPromise = WPromise;
    }
    if (typeof module !== 'undefined' && typeof module.exports!=='undefined') {
        module.exports = WPromise
    }
})()
